vim.keymap.set('i', '<A-d>', '<C-o>de')
vim.keymap.set('i', '<C-k>', '<C-o>D')

vim.keymap.set('i', '<C-g>itd', '<c-o><cmd>Today<cr>')

vim.keymap.set({ 'n', 'x' }, ' ', ':')

vim.keymap.set({ 'n', 'x' }, '+', '<C-E>')
vim.keymap.set({ 'n', 'x' }, '_', '<C-Y>')
vim.keymap.set({ 'n', 'x' }, '<C-J>', '<C-D>')
vim.keymap.set({ 'n', 'x' }, '<C-K>', '<C-U>')

vim.keymap.set('n', '<C-Tab>', '<C-W>w')

-- case sensitive search, useful when `ignorecase` is set
vim.keymap.set({ 'n', 'x' }, 'g/', [[/\C]])
-- this can be paired with a shell script that restarts neovim every time it
-- exits with the specific code, useful when one need to re-open neovim many
-- times
vim.keymap.set('n', '<leader>Xr', '<cmd>cq 42<cr>')
vim.keymap.set('n', '<leader>Xq', '<cmd>Qa<cr>')

vim.keymap.set('x', '<C-Up>', function() require('lin.utils').visual_move_lines('up') end)
vim.keymap.set('x', '<C-Down>', function() require('lin.utils').visual_move_lines('down') end)

vim.keymap.set('n', '<leader>qo', '<cmd>copen<cr>')
vim.keymap.set('n', '<leader>qc', '<cmd>cclose<cr>')
vim.keymap.set('n', '<leader>qn', '<cmd>cnext<cr>')
vim.keymap.set('n', '<leader>qp', '<cmd>cprevious<cr>')
vim.keymap.set('n', '<leader>qf', '<cmd>cfirst<cr>')
vim.keymap.set('n', '<leader>ql', '<cmd>clast<cr>')

vim.keymap.set('n', '<leader>ds', vim.diagnostic.open_float)
vim.keymap.set('n', '<leader>dp', vim.diagnostic.goto_prev)
vim.keymap.set('n', '<leader>dn', vim.diagnostic.goto_next)
vim.keymap.set('n', '<leader>dl', vim.diagnostic.setloclist)

vim.keymap.set('t', '<C-Tab>', [[<C-\><C-N>]])
vim.keymap.set('t', '<C-`>', [[<C-\><C-N><cmd>quit<cr>]])

---@type table<number, number>
local tab_term = {}
vim.keymap.set('n', '<C-`>', function()
    local utils = require('lin.utils')
    local api = vim.api

    local tabpage = api.nvim_get_current_tabpage()
    local term = tab_term[tabpage]

    if term and api.nvim_buf_is_loaded(term) then
        for _, win in ipairs(utils.cur_tab_focusable_wins()) do
            if api.nvim_win_get_buf(win) == term then
                api.nvim_win_hide(win)
                return
            end
        end
        vim.cmd('belowright split')
        vim.fn.feedkeys('i', 't')
        api.nvim_win_set_buf(0, term)
    else
        vim.cmd('belowright split +term')
        vim.fn.feedkeys('i', 't')
        api.nvim_buf_set_name(0, 'Tab-' .. tabpage .. '/Terminal')
        tab_term[tabpage] = api.nvim_get_current_buf()
    end
end)

---@type integer?
local last_tab
---@type table<string,integer>
local tab_marked = {}
vim.keymap.set({ 'n', 'x' }, 'x', function()
    local is_reserved = function(char)
        local used = { ',', 'j', 'k', 'l', 'y', 'n', 'm' }
        return (char >= '1' and char <= '9')
            or vim.list_contains(used, char)
    end

    local cmd_jump = function(cmd)
        last_tab = vim.fn.tabpagenr()
        vim.cmd(cmd)
    end

    local inp = vim.fn.getcharstr()

    if inp == ',' then
        if last_tab then
            cmd_jump(tostring(last_tab) .. 'tabnext')
        end
    elseif inp == 'j' then
        cmd_jump('tabnext')
    elseif inp == 'k' then
        cmd_jump('tabprev')
    elseif inp == 'l' then
        cmd_jump('tablast')
    elseif inp == 'y' then
        cmd_jump('tab vsp')
    elseif inp >= '1' and inp <= '9' then
        cmd_jump(inp .. 'tabnext')
    elseif inp == 'n' then
        local inp_new = vim.fn.getcharstr()
        local cmd_table = { n = 'tabnew', p = '-tabnew', f = '0tabnew', l = '$tabnew' }
        if cmd_table[inp_new] then
            cmd_jump(cmd_table[inp_new])
        end
    elseif inp == 'm' then
        local inp_mark = vim.fn.getcharstr()
        if not is_reserved(inp_mark) then
            tab_marked[inp_mark] = vim.api.nvim_get_current_tabpage()
        end
    else
        local tab = tab_marked[inp]
        if tab then
            last_tab = vim.fn.tabpagenr()
            vim.api.nvim_set_current_tabpage(tab)
        end
    end
end)
